home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 …ember: Reference Library / Apple Developer Reference Library (December 1999) (Disk 1).iso / pc / technical documentation / develop / develop issue 26 / develop issue 26 code / truffles - display mgr. / sprocket / sources / semaphores.cp < prev    next >
Encoding:
Text File  |  1995-04-14  |  24.1 KB  |  739 lines

  1. #include "Semaphores.h"
  2. #include <AppleEvents.h>
  3. #include <AERegistry.h>
  4. #include <Errors.h>
  5.  
  6. //----------------------------------------------------------------------------------------
  7. // TimeExpired
  8. //
  9. // This routine checks to see if the timer has run out; it handles the case when
  10. // the stopTime calculation (startTime + timeout) wraps around (overflows a long int).
  11. // This only happens every 771 days or so, but Mike Gough said that I wasn't a stud
  12. // unless I handled this case.
  13. //----------------------------------------------------------------------------------------
  14. Boolean TimeExpired(unsigned long currentTime, unsigned long startTime, unsigned long stopTime)
  15. {
  16.     //
  17.     // First adjust time so that the origin is at 'startTime'
  18.     //
  19.     stopTime -= startTime;
  20.     currentTime -= startTime;
  21.     
  22.     //
  23.     // Then, it is a simple matter to compare the current
  24.     // time value with the time that we want to stop at.
  25.     //
  26.     return currentTime >= stopTime;
  27. }
  28.  
  29. //----------------------------------------------------------------------------------------
  30. // CloserTick
  31. //
  32. // It is also important to know which of two ticks will happen first, starting from
  33. // a given tick count.  If one of the ticks == currentTime, we consider that event
  34. // to have already gone by (hence the extra +1's), and won't be encountered again until
  35. // the timer wraps all the way around ~771 days later.
  36. //----------------------------------------------------------------------------------------
  37. unsigned long CloserTick(unsigned long currentTime, unsigned long tick1, unsigned long tick2)
  38. {
  39.     //
  40.     // Once again, move the beginning of time, this time to 'currentTime + 1'
  41.     //
  42.     tick1 -= (currentTime + 1);
  43.     tick2 -= (currentTime + 1);
  44.     
  45.     //
  46.     // Once we've converted to a zero-based system, it's easy to compare
  47.     // the two times.  Convert back to the appropriate time base by adding
  48.     // currentTime + 1 back onto the tick after we've determined which will
  49.     // happen sooner.
  50.     //
  51.     if(tick1 < tick2)
  52.         return tick1 + currentTime + 1;
  53.     else
  54.         return tick2 + currentTime + 1;
  55. }
  56.  
  57. //========================================================================================
  58. // CLASS TThreadIDQueue
  59. //========================================================================================
  60.  
  61. #define kThreadQueueAllocSize 4
  62.  
  63. //----------------------------------------------------------------------------------------
  64. // TThreadIDQueue::~TThreadIDQueue
  65. //----------------------------------------------------------------------------------------
  66. TThreadIDQueue::~TThreadIDQueue()
  67. {
  68.     if(fThreadQueue != nil)
  69.     {
  70.         DisposeHandle((Handle)fThreadQueue);
  71.         fThreadQueue = nil;
  72.     }
  73. } // TThreadIDQueue::~TThreadIDQueue
  74.  
  75. //----------------------------------------------------------------------------------------
  76. // TThreadIDQueue::Enqueue
  77. //----------------------------------------------------------------------------------------
  78. OSErr TThreadIDQueue::Enqueue(ThreadID threadToAdd)
  79. {
  80.     OSErr err = this->InsureThreadQueueHasFreeSpace();
  81.     if(err == noErr)
  82.     {
  83.         (*fThreadQueue)[fThreadCount] = threadToAdd;
  84.         ++fThreadCount;
  85.     }
  86.     return err;
  87. } // TThreadIDQueue::Enqueue
  88.  
  89. //----------------------------------------------------------------------------------------
  90. // TThreadIDQueue::Dequeue
  91. //----------------------------------------------------------------------------------------
  92. ThreadID TThreadIDQueue::Dequeue()
  93. {
  94.     ThreadID resultThreadID = kNoThreadID;
  95.  
  96.     if(fThreadCount > 0)
  97.     {
  98.         resultThreadID = (*fThreadQueue)[0];
  99.         --fThreadCount;
  100.         for(long i=0;i<fThreadCount;++i)
  101.             (*fThreadQueue)[i] = (*fThreadQueue)[i + 1];
  102.         (*fThreadQueue)[fThreadCount] = kNoThreadID;
  103.     }
  104.  
  105.     return resultThreadID;
  106. } // TThreadIDQueue::Dequeue
  107.  
  108. //----------------------------------------------------------------------------------------
  109. // TThreadIDQueue::InsureThreadQueueHasFreeSpace
  110. //----------------------------------------------------------------------------------------
  111. OSErr TThreadIDQueue::InsureThreadQueueHasFreeSpace()
  112. {
  113.     OSErr err = noErr;
  114.     
  115.     if(fThreadQueue == nil)
  116.     {
  117.         if((fThreadCount > 0) || (fReservedSpace > 0))
  118.             err = errAEEventFailed;
  119.         else
  120.         {
  121.             fThreadQueue = (ThreadID**) NewHandle( sizeof(ThreadID) * kThreadQueueAllocSize );
  122.             err = MemError();
  123.             if(err == noErr)
  124.                 fReservedSpace = kThreadQueueAllocSize;
  125.         }
  126.     }
  127.     else if(fThreadCount >= fReservedSpace)
  128.     {
  129.         if(fThreadCount > fReservedSpace)
  130.             err = errAEEventFailed;
  131.         else
  132.         {
  133.             SetHandleSize((Handle)fThreadQueue, sizeof(ThreadID) * (kThreadQueueAllocSize + fReservedSpace));
  134.             err = MemError();
  135.             if(err == noErr)
  136.                 fReservedSpace += kThreadQueueAllocSize;
  137.         }
  138.     }
  139.     
  140.     return err;
  141. } // TThreadIDQueue::InsureThreadQueueHasFreeSpace
  142.  
  143. //========================================================================================
  144. // CLASS TSemaphore
  145. //========================================================================================
  146.  
  147. TSemaphore*        TSemaphore::gFirstSemaphore                = nil;
  148.  
  149. long            TSemaphore::gSemaphoreIDSequence        = kAnySemaphoreID + 1;
  150. long            TSemaphore::gSemaphoreDefaultTimeout    = kNeverTimeoutSemaphore;
  151. long            TSemaphore::gSemaphoreDefaultMaxWait    = kNeverTimeoutSemaphore;
  152.  
  153. unsigned long    TSemaphore::gLastTimeoutTest            = 0;
  154. unsigned long    TSemaphore::gLastIdleTick                = 0;
  155. unsigned long    TSemaphore::gNextTimeoutTest            = 1;
  156.  
  157. //----------------------------------------------------------------------------------------
  158. // TSemaphore::TSemaphore
  159. //
  160. // Add the semaphore to a global list so we can look up semaphores later by ID.
  161. //----------------------------------------------------------------------------------------
  162. TSemaphore::TSemaphore(long resourceCount /* = 1 */, long semaphoreID /* = kAnySemaphoreID*/)
  163. {
  164.     if (semaphoreID == kAnySemaphoreID)
  165.         semaphoreID = TSemaphore::NewUniqueSemaphoreID();
  166.     
  167.     fResourceCount = resourceCount;
  168.     fSemaphoreID = semaphoreID;
  169.  
  170.     //
  171.     // We have one reference (the person who created the semaphore)
  172.     // but no owner yet
  173.     //
  174.     fReferenceCount = 1;
  175.     fOwnerThread = kNoThreadID;
  176.     fOwnerCount = 0;
  177.     fDisposed = false;
  178.     fFailOnWakeup = noErr;
  179.     
  180.     fSemaphoreTimeoutValue = gSemaphoreDefaultTimeout;
  181.     fSemaphoreMaxWaitTime = gSemaphoreDefaultMaxWait;
  182.     fBlockStartTick = TickCount();
  183.     this->ResetTimeoutTimer();
  184.     
  185.     fNextSemaphore = gFirstSemaphore;
  186.     fPreviousSemaphore = nil;
  187.     if(fNextSemaphore != nil)
  188.         fNextSemaphore->fPreviousSemaphore = this;
  189.     gFirstSemaphore = this;
  190. }
  191.  
  192. //----------------------------------------------------------------------------------------
  193. // TSemaphore::~TSemaphore: 
  194. //
  195. // First, remove ourselves from the global linked list of all semaphores.  Then,
  196. // wake up all threads that are still blocked on us before we go away so they won’t
  197. // be stranded, asleep forever.
  198. //----------------------------------------------------------------------------------------
  199. TSemaphore::~TSemaphore()
  200. {
  201.     this->RemoveFromGlobalSemaphoreList();
  202. }
  203.  
  204. //----------------------------------------------------------------------------------------
  205. // TSemaphore::InitializeGlobals
  206. //----------------------------------------------------------------------------------------
  207. void TSemaphore::InitializeGlobals()
  208. {
  209.     gFirstSemaphore                = nil;
  210.  
  211.     gSemaphoreIDSequence        = kAnySemaphoreID + 1;
  212.     gSemaphoreDefaultTimeout    = kNeverTimeoutSemaphore;
  213.     gSemaphoreDefaultMaxWait    = kNeverTimeoutSemaphore;
  214.  
  215.     gLastTimeoutTest            = 0;
  216.     gLastIdleTick                = 0;
  217.     gNextTimeoutTest            = 1;
  218. }
  219.  
  220. //----------------------------------------------------------------------------------------
  221. // TSemaphore::RemoveFromGlobalSemaphoreList
  222. //----------------------------------------------------------------------------------------
  223. void TSemaphore::RemoveFromGlobalSemaphoreList()
  224. {
  225.     if((fPreviousSemaphore != nil) || (fNextSemaphore != nil) || (gFirstSemaphore == this))
  226.     {
  227.         //
  228.         // Either gFirstSemaphore == this or fPreviousSemaphore != nil
  229.         //
  230.         if (gFirstSemaphore == this)
  231.             gFirstSemaphore = fNextSemaphore;
  232.         else if (fPreviousSemaphore != nil)
  233.             fPreviousSemaphore->fNextSemaphore = fNextSemaphore;
  234.         else
  235.             DebugStr("\pRemoveFromGlobalSemaphoreList semaphore list corrupt");
  236.         
  237.         if (fNextSemaphore != nil)
  238.             fNextSemaphore->fPreviousSemaphore = fPreviousSemaphore;
  239.         
  240.         fPreviousSemaphore = nil;
  241.         fNextSemaphore = nil;
  242.     }
  243. }
  244.  
  245. //----------------------------------------------------------------------------------------
  246. // TSemaphore::ReleaseReference
  247. //
  248. // Delete this semaphore iff there are no references to it.
  249. // Usually, clients should call either 'Dispose' (the owner of the semaphore should
  250. // dispose it) or 'Release' (any thread that grabs a semaphore should release it).
  251. //----------------------------------------------------------------------------------------
  252. void TSemaphore::ReleaseReference()
  253. {
  254.     --fReferenceCount;
  255.     //
  256.     // By the time our reference count goes to zero, fDisposed should
  257.     // be true and we should no longer be in the global semaphore list.
  258.     //
  259. //    if(fReferenceCount == 0)
  260. //        delete this;
  261. } // TSemaphore::ReleaseReference
  262.  
  263. //----------------------------------------------------------------------------------------
  264. // TSemaphore::Dispose
  265. //
  266. // Delete this semaphore iff there are no threads blocked on it.  If there are
  267. // blocked threads, then wait until the threads all wake up before disposing this
  268. // semaphore.
  269. //----------------------------------------------------------------------------------------
  270. void TSemaphore::Dispose()
  271. {
  272.     this->ReleaseAllThreads();
  273.     this->RemoveFromGlobalSemaphoreList();
  274.     fDisposed = true;
  275.     this->ReleaseReference();
  276. }
  277.  
  278. //----------------------------------------------------------------------------------------
  279. // TSemaphore::NewUniqueSemaphoreID: 
  280. // A static method to generate a unique ID for a semaphore.  Unique IDs are generated 
  281. // simply by starting at zero and counting up.
  282. //----------------------------------------------------------------------------------------
  283. long TSemaphore::NewUniqueSemaphoreID()
  284. {
  285.     ++gSemaphoreIDSequence;
  286.     return gSemaphoreIDSequence;
  287. }
  288.  
  289.  
  290. //----------------------------------------------------------------------------------------
  291. // TSemaphore::FindSemaphore: 
  292. //----------------------------------------------------------------------------------------
  293. TSemaphore*    TSemaphore::FindSemaphore(long semaphoreID, Boolean createIfNotFound /* = false */, long resourceCount /* = 1 */)
  294. {
  295.     TSemaphore* resultSemaphore = gFirstSemaphore;
  296.     
  297.     if(gFirstSemaphore != nil)
  298.         if(gFirstSemaphore->fPreviousSemaphore != nil)
  299.             DebugStr("\pFirst semaphore in list corrupt!");
  300.     
  301.     while ((resultSemaphore != nil) && (resultSemaphore->fSemaphoreID != semaphoreID))
  302.     {
  303.         if(resultSemaphore->fNextSemaphore != nil)
  304.             if(resultSemaphore->fNextSemaphore->fPreviousSemaphore != resultSemaphore)
  305.                 DebugStr("\pSemaphore linked list corrupt!");
  306.         
  307.         resultSemaphore = resultSemaphore->fNextSemaphore;
  308.     }
  309.     
  310.     if((resultSemaphore == nil) && (createIfNotFound == true))
  311.         resultSemaphore = new TSemaphore(resourceCount, semaphoreID);
  312.     
  313.     return resultSemaphore;
  314. }
  315.     
  316.  
  317. //----------------------------------------------------------------------------------------
  318. // TSemaphore::Idle
  319. //
  320. // Call Periodicly to test for blocked semaphores whose time has expired
  321. //----------------------------------------------------------------------------------------
  322. void TSemaphore::Idle()
  323. {
  324.     unsigned long currentTime = TickCount();
  325.     long semaphoreCount = 0;
  326.     long gracePeriod = currentTime - gLastIdleTick;
  327.     
  328.     //
  329.     // If we have been away for a long time, then allow clients
  330.     // a grace period; the idea being, that the user or some
  331.     // selfish process probably locked up the machine for a long
  332.     // time, so it's likely that no one (client or server) got
  333.     // any work done.  Even if the server is on some other machine,
  334.     // we still want to add in the grace period; it would be really
  335.     // bad to time out after a long lock-out if the reply was sitting
  336.     // in the AppleEvent manager's queue, waiting for some time to
  337.     // be processed.
  338.     //
  339.     // If we weren't gone for at least half a second, though, then we'll
  340.     // assume that the machine is running just fine.
  341.     //
  342.     if((gracePeriod < 30) || (gLastIdleTick == 0))
  343.         gracePeriod = 0;
  344.     gLastIdleTick = currentTime;
  345.     
  346.     //
  347.     // Don't walk the semaphore list until it's time
  348.     //
  349.     if(TimeExpired(currentTime, gLastTimeoutTest, gNextTimeoutTest))
  350.     {
  351.         // DebugStr("\pTSemaphore::Idle decided to do work");
  352.         
  353.         //
  354.         // Reset gLastTimeoutTest to currentTime to record that this
  355.         // is the time that we did the last test.  Reset gNextTimeoutTest
  356.         // to be one tick before that, the longest time we could
  357.         // possably wait before doing another test (forever!).  In the
  358.         // "forever" case, gNextTimeoutTest will be reset when new
  359.         // semaphores are created.
  360.         //
  361.         gLastTimeoutTest = currentTime;
  362.         gNextTimeoutTest = currentTime - 1;
  363.         
  364.         TSemaphore* semaphore = gFirstSemaphore;
  365.         
  366.         while(semaphore != nil)
  367.         {
  368.             ++semaphoreCount;
  369.             TSemaphore* nextSemaphore = semaphore->fNextSemaphore;
  370.             
  371.             //
  372.             // If we've been away for a while, then allow the
  373.             // semaphore to adjust itself.
  374.             //
  375.             if(gracePeriod)
  376.                 semaphore->AddGracePeriod(gracePeriod);
  377.             
  378.             if(semaphore->CheckIfTimerExpired(currentTime) == false)
  379.             {
  380.                 // DebugStr("\pFound a semaphore that has not timed out");
  381.                 
  382.                 //
  383.                 // For next time through the loop:  test to see
  384.                 // if the timeout tick for this semaphore is going
  385.                 // to happen sooner than any other timeout value
  386.                 // we've calculated yet.  Doing this test now will
  387.                 // let us avoid walking the list of semaphores until
  388.                 // it's time for one of them to expire.  We don't
  389.                 // care if the next-in-line semaphore goes away
  390.                 // before we test again; this is just a best-case
  391.                 // estimate.
  392.                 //
  393.                 gNextTimeoutTest = CloserTick(currentTime, gNextTimeoutTest, semaphore->TimeoutTick());
  394.             }
  395.             else
  396.             {
  397.                 //
  398.                 // If the semaphore's time has come, cancel the threads
  399.                 // that are blocked on it.
  400.                 //
  401.                 semaphore->SemaphoreTimedOut();
  402.             }
  403.             
  404.             semaphore = nextSemaphore;
  405.         }
  406.     }
  407. }
  408.  
  409. //----------------------------------------------------------------------------------------
  410. // TSemaphore::ThreadDied
  411. //
  412. // If a thread is killed from some other thread, and the killed thread might
  413. // be the owner of some semaphore, then the killer must call this routine so
  414. // that the semaphore can be released.
  415. //----------------------------------------------------------------------------------------
  416. void TSemaphore::ThreadDied(ThreadID threadID)
  417. {
  418.     TSemaphore* semaphore = gFirstSemaphore;
  419.     while(semaphore != nil)
  420.     {
  421.         TSemaphore* nextSemaphore = semaphore->fNextSemaphore;
  422.         
  423.         //
  424.         // Release this semaphore iff it was owned by the thread that died
  425.         //
  426.         semaphore->Release(threadID);
  427.         semaphore = nextSemaphore;
  428.     }
  429. }
  430.  
  431. //----------------------------------------------------------------------------------------
  432. // TSemaphore::Grab: 
  433. //
  434. // If the specified thread does not already have ownership of this semaphore, enqueue it.
  435. // If other threads are already waiting, put the thread to sleep (to be woken up when it
  436. // gets to the head of the queue).  By default, the thread grabbing the semaphore is the
  437. // current thread.
  438. //----------------------------------------------------------------------------------------
  439. OSErr TSemaphore::Grab(ThreadID grabbingThread /* = kCurrentThreadID */)
  440. {
  441.     OSErr err = noErr;
  442.  
  443.     //
  444.     // Every call to 'Grab' should be balanced by a call to 'release'
  445.     //
  446.     ++fReferenceCount;
  447.     
  448.     //
  449.     // This semaphore isn't good for much once it's been disposed.
  450.     // Similarly, if a semaphore has timed out and is causing all
  451.     // of its blocked threads to wake up (fFailOnWakeup != noErr),
  452.     // then we shouldn't let new threads in either.
  453.     //
  454.     if(fFailOnWakeup != noErr)
  455.         err = fFailOnWakeup;
  456.     else if(fDisposed)
  457.         err = errAEEventFailed;
  458.     else
  459.     {    
  460.         //
  461.         // By default the current thread grabs the semaphore
  462.         //
  463.         if (grabbingThread == kCurrentThreadID)
  464.             GetCurrentThread(&grabbingThread);
  465.  
  466.         //
  467.         // Short-circuit if we already own the semaphore
  468.         // (Note:  if the semaphore sontrolled multiple
  469.         // resources, then it may be possible for one thread
  470.         // to grab the same resource more than once.)
  471.         //
  472.         if (grabbingThread == fOwnerThread)
  473.             return noErr;
  474.         
  475.         //
  476.         // If the semaphore is available, then we'll let this thread
  477.         // grab it.
  478.         //    
  479.         if (fOwnerCount < fResourceCount)
  480.         {
  481.             //
  482.             // There shouldn’t be an owner if we’re about to grab it;
  483.             // set the owner, and mark the semaphore as being unavailable
  484.             //
  485.             // Assert(fOwnerThread == kNoThreadID);
  486.             fOwnerThread = grabbingThread;
  487.             ++fOwnerCount;
  488.         }
  489.         //
  490.         // If the semaphore isn't available, then block the grabbing thread
  491.         //
  492.         else    
  493.         {
  494.             //
  495.             // Enqueue the thread, increment the count of threads blocked
  496.             // on this semaphore (often different than the count of threads
  497.             // queued, since at wake-up time threads will be dequeued and
  498.             // woken up, but won't fall out of 'SetThreadState' until the
  499.             // next time they're scheduled)
  500.             //
  501.             err = fBlockedThreads.Enqueue(grabbingThread);
  502.             if(err == noErr)
  503.                 err = SetThreadState(grabbingThread, kStoppedThreadState, kNoThreadID);
  504.             
  505.             //
  506.             // If someone has set our automatic-failure signal, then
  507.             // make note of it; this error should take precedence over
  508.             // any returned by 'SetThreadState'.
  509.             //
  510.             if(fFailOnWakeup != noErr)
  511.             {
  512.                 err = fFailOnWakeup;
  513.             }
  514.         }
  515.     }
  516.     
  517.     return err;
  518. }
  519.  
  520. //----------------------------------------------------------------------------------------
  521. // TSemaphore::Release: 
  522. //
  523. // Remove the reference that this thread has to this semaphore
  524. //----------------------------------------------------------------------------------------
  525. void TSemaphore::Release(ThreadID threadID /* = kCurrentThreadID */)
  526. {
  527.     //
  528.     // If no thread is specified, then assume that the current thread
  529.     // is being released.
  530.     //
  531.     if (threadID == kCurrentThreadID)
  532.         GetCurrentThread(&threadID);
  533.  
  534.     //
  535.     // If we own this semaphore, then let someone else have it
  536.     //
  537.     // ••• We should require that the thread being released is
  538.     // either the owner or in the list of blocked threads.  If
  539.     // it is in the list of blocked threads, then we should wake
  540.     // it up if possible.
  541.     //
  542.     if(fOwnerThread == threadID)
  543.     {
  544.         fOwnerThread = this->ReleaseOneThread();
  545.     }
  546.     
  547.     //
  548.     // Decrement our reference count
  549.     //
  550.     this->ReleaseReference();
  551. }
  552.  
  553. //----------------------------------------------------------------------------------------
  554. // TSemaphore::ReleaseOneThread: 
  555. //
  556. // If the thread queue is non-empty, wake up the thread that is now at the head of the
  557. // queue.  If the queue is empty, do nothing.
  558. //----------------------------------------------------------------------------------------
  559. ThreadID TSemaphore::ReleaseOneThread()
  560. {
  561.     ThreadID releasedThread = kNoThreadID;
  562.     
  563.     //
  564.     // Probably not necessary, but let's make sure that this
  565.     // semaphore is never deleted while we're working with it.
  566.     //
  567.     ++fReferenceCount;
  568.     
  569.     //
  570.     // Keep working until we find a thread to wake up,
  571.     // or we run out of threads that are blocked on us
  572.     //
  573.     while(fBlockedThreads.QueuedThreads() > 0)
  574.     {
  575.         //
  576.         // Take the first thread waiting in line and wake it up
  577.         //
  578.         releasedThread = fBlockedThreads.Dequeue();
  579.         
  580.         //
  581.         // If we have a thread, try to wake it up.  Stop working
  582.         // if we can; if we can't wake it up (maybe someone killed
  583.         // it already), then try to pop off the next thread in the list.
  584.         //
  585.         if (releasedThread != kNoThreadID)
  586.         {
  587.             //
  588.             // If the thread can't be woken up, then it will never
  589.             // fall out from TSemaphore::Grab; in that case, decrement
  590.             // the count of blocked threads.
  591.             //
  592.             if(SetThreadState(releasedThread, kReadyThreadState, kNoThreadID) == noErr)
  593.                 break;
  594.             else
  595.                 this->ReleaseReference();
  596.         }
  597.     }
  598.     
  599.     //
  600.     // Release the reference we grabbed
  601.     //
  602.     this->ReleaseReference();
  603.     
  604.     return releasedThread;
  605. }
  606.  
  607. //----------------------------------------------------------------------------------------
  608. // TSemaphore::ReleaseAllThreads: 
  609. // Release everyone who’s blocked on us by simply releasing threads until no one is left.
  610. //----------------------------------------------------------------------------------------
  611. void TSemaphore::ReleaseAllThreads()
  612. {
  613.     //
  614.     // Probably not necessary, but let's make sure that this
  615.     // semaphore is never deleted while we're working with it.
  616.     //
  617.     ++fReferenceCount;
  618. //    while (fBlockedThreads.QueuedThreads() > 0)
  619.         this->ReleaseOneThread();
  620.     
  621.     //
  622.     // Release the reference we grabbed
  623.     //
  624.     this->ReleaseReference();
  625. }
  626.  
  627. //----------------------------------------------------------------------------------------
  628. // TSemaphore::CheckIfTimerExpired
  629. //
  630. // CurrentTime is just 'TickCount', but since this routine is always called from a
  631. // loop, I didn't think there was any point in calling it over and over again.
  632. //
  633. // If the timer has expired, then set fFailOnWakeup to errAETimeout and wake up
  634. // all of the blocked threads.
  635. //----------------------------------------------------------------------------------------
  636. Boolean TSemaphore::CheckIfTimerExpired(unsigned long currentTime)
  637. {    
  638.     //
  639.     // In order for our timer to run out, one of fSemaphoreTimeoutValue
  640.     // or fSemaphoreMaxWaitTime must be something other than kNeverTimeoutSemaphore,
  641.     // AND the time must have expired for this semaphore.
  642.     //
  643.     return (    ((fSemaphoreTimeoutValue != kNeverTimeoutSemaphore) || (fSemaphoreMaxWaitTime != kNeverTimeoutSemaphore))
  644.                 && TimeExpired(fBlockStartTick, fTimeoutTick, currentTime)    );
  645. }
  646.  
  647. //----------------------------------------------------------------------------------------
  648. // TSemaphore::SemaphoreTimedOut
  649. //
  650. // If the timer expired, deal with it.
  651. //----------------------------------------------------------------------------------------
  652. void TSemaphore::SemaphoreTimedOut()
  653. {
  654.     // DebugStr("\pSemaphore timed out!  Wake up blocked threads");
  655.     
  656.     fFailOnWakeup = errAETimeout;
  657.     this->ReleaseAllThreads();
  658. }
  659.  
  660. //----------------------------------------------------------------------------------------
  661. // TSemaphore::AddGracePeriod
  662. //
  663. // Acount for the fact that the machine may have been locked up for a while by
  664. // user actions or what-have-you.
  665. //----------------------------------------------------------------------------------------
  666. void TSemaphore::AddGracePeriod(long gracePeriod)
  667. {
  668.     //
  669.     // Pretend that our timer was reset 'gracePeriod' ticks
  670.     // later than it actually was.
  671.     //
  672.     fTimeoutTick += gracePeriod;
  673. } // TSemaphore::AddGracePeriod
  674.  
  675. //----------------------------------------------------------------------------------------
  676. // TSemaphore::ResetTimeoutTimer
  677. //
  678. // Reset the timer, but don't allow it to go past 'maxTimoutValue'.
  679. //----------------------------------------------------------------------------------------
  680. void TSemaphore::ResetTimeoutTimer()
  681. {
  682.     unsigned long currentTime = TickCount();
  683.     unsigned long newTimeoutValue = fSemaphoreTimeoutValue;
  684.         
  685.     //
  686.     // Pin the timeout value rather than the timeout tick,
  687.     // so we don't get messed up by overflow problems.
  688.     //
  689.     // Note that if we have passed '(fBlockStartTick + fSemaphoreMaxWaitTime)',
  690.     // then maxTimeoutValue will be negative.  This is what we
  691.     // want:  the semaphore should time out on the next call to
  692.     // 'CheckIfTimerExpired'.
  693.     //
  694.     if(fSemaphoreMaxWaitTime != kNeverTimeoutSemaphore)
  695.     {
  696.         unsigned long maxTimeoutValue = (fBlockStartTick + fSemaphoreMaxWaitTime) - currentTime;
  697.         if(newTimeoutValue > maxTimeoutValue)
  698.             newTimeoutValue = maxTimeoutValue;
  699.     }
  700.     
  701.     fTimeoutTick = currentTime + newTimeoutValue;
  702.     
  703.     //
  704.     // Recalculate the next time that we're going to test
  705.     // for Semaphore timeouts (just in case gNextTimeoutTest
  706.     // is off on the 'forever' side of gLastTimeoutTest).
  707.     //
  708.     gNextTimeoutTest = CloserTick(currentTime, gNextTimeoutTest, fTimeoutTick);
  709. }
  710.  
  711. //----------------------------------------------------------------------------------------
  712. // TSemaphore::SetSemaphoreTimoutValue
  713. //
  714. // Specify some timeout value other than the default for this semaphore.
  715. //
  716. // Don't call 'SetSemaphoreTimoutValue' at arbitrary times, because it also resets the
  717. // timeout timer.
  718. //----------------------------------------------------------------------------------------
  719. void TSemaphore::SetSemaphoreTimoutValue(long timeoutValue)
  720. {
  721.     fSemaphoreTimeoutValue = timeoutValue;
  722.     this->ResetTimeoutTimer();
  723. }
  724.  
  725. //----------------------------------------------------------------------------------------
  726. // TSemaphore::SetSemaphoreMaxWaitTime
  727. //
  728. // Specify some maximum wait time other than the default for this semaphore.
  729. //
  730. // Don't call 'SetSemaphoreMaxWaitTime' at arbitrary times, because it also resets the
  731. // timeout timer.
  732. //----------------------------------------------------------------------------------------
  733. void TSemaphore::SetSemaphoreMaxWaitTime(long timeoutValue)
  734. {
  735.     fSemaphoreMaxWaitTime = timeoutValue;
  736.     this->ResetTimeoutTimer();
  737. }
  738.  
  739.